// **************************************************************
// COPYRIGHT(c)2020, Xidian University
// All rights reserved.
//
// IP LIB INDEX :  
// IP Name      :      
// File name    : pingpang_ctrl.v
// Module name  : pingpang_ctrl
// Full name    :  
//
// Author       : Hbing
// Email        : 2629029232@qq.com
// Data         : 2020/9/12
// Version      : V 1.0 
// 
// Abstract     : 
// Called by    :  
// 
// Modification history
// -----------------------------------------------------------------
// add rden MUX
// 
// 
// *****************************************************************
 `include "top_define.v"
// *******************
// TIMESCALE
// ******************* 
`timescale 1ns/1ps 

// *******************
// DESCRIPTION
// *******************
// 3*3交叉节点，但是为了防止阻塞，第三列交叉节点中的乒乓RAM翻倍，分别存port2,port3目的端口的数据帧
// 
// 
//*******************
//DEFINE(s)
//*******************
//`define UDLY 1    //Unit delay, for non-blocking assignments in sequential logic

//*******************
//DEFINE MODULE PORT
//*******************
module pingpang_ctrl(
    //sysrem input/output
    input  wire         clk             ,
    input  wire         rst_n           ,
    input  wire [9:0]   ram_2p_cfg_register,
    //for ram writing
    (*mark_debug = "true"*) //input  wire         dpram_en        ,
    (*mark_debug = "true"*) //input  wire         dpram_num       ,
    input  wire [  5:0] rx_address_dpram,
    input  wire [255:0] rx_data_dpram_0 ,
    input  wire [255:0] rx_data_dpram_1 ,
    input  wire         rx_wren_dpram_0 ,
    input  wire         rx_wren_dpram_1 ,
    (*mark_debug = "true"*) output wire         buffer_available,
    //for ram_reading
    input  wire [  5:0] address         ,
    (*mark_debug = "true"*) input  wire         rden            ,
    output wire [255:0] qout            ,
    (*mark_debug = "true"*) output wire         frame_available ,
    (*mark_debug = "true"*) input  wire         ack             
    // output reg          slave_clr_a     ,
    // output reg          slave_clr_b     
    // output reg  [  1:0] buffer_state    

);
//*******************
//DEFINE LOCAL PARAMETER
//*******************
//parameter(s)
// localparam IDLE = 2'b00;
localparam A    = 2'b01;
localparam B    = 2'b10;
//交叉节点乒乓RAM写入w256-d64
//6'b000000--队列号-帧长-目的端口列表
//6'b000001--SRAM_memory读出的数据
//*********************
//INNER SIGNAL DECLARATION
//*********************
//REGS
//状态机
reg [1:0] c_state,n_state;
//ack信号打拍
reg ack_ff1;
//乒乓RAM状态
(*mark_debug = "true"*) reg  [  1:0] buffer_state;
//数据寄存
// reg         rx_wren_dpram_0_ff1;
// reg         rx_wren_dpram_1_ff1;
// reg [  5:0] rx_address_dpram_ff1;
// reg [255:0] rx_data_dpram_0_ff1;
// reg [255:0] rx_data_dpram_1_ff1;
//WIRES
//取ack信号的上升沿--一帧存储完毕
// wire ack_pos;
//乒乓RAM读数据
wire [255:0] dpram_A_q_b; 
wire [255:0] dpram_B_q_b;
wire rden_0,rden_1;
// wire frame_available_tmp;
reg frame_available_0;
reg frame_available_1;
//*********************
//INSTANTCE MODULE
//*********************

//*********************
//MAIN CORE
//********************* 
//状态机--AB乒乓RAM输出数据的切换
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        c_state <= A;
    end
    else begin
        c_state <= n_state;
    end
end

always @(*)
begin
    case(c_state)
        A:
        begin
           if (ack_ff1 == 1'b1) begin
                n_state = B;
            end
            else begin
                n_state = A;
            end
        end
        B:
        begin
            if (ack_ff1 == 1'b1) begin
                n_state = A;
            end
            else begin
                n_state = B;
            end
        end
        default:
        begin
            n_state = A;
        end
    endcase
end

//产生ack信号的上升沿 处理ack对 读AB数据选择状态机和更新数据有效信号的矛盾
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        ack_ff1 <= 1'b0;
    end
    else begin
        ack_ff1 <= ack;
    end
end

// assign ack_pos = ack & (~ack_ff1);

//A口缓存区状态
//  A数据发送完拉低
//  写入0地址数据拉高
//  同时读写状态更新有没有问题？ 优先级
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        buffer_state[0] <= 1'b0;
    else if((c_state == A) && (ack == 1'b1))
        buffer_state[0] <= 1'b0;
    else if((c_state == B) && (ack == 1'b1) && (buffer_state[1]!=1'b1))
        buffer_state[0] <= 1'b0;
    else if((rx_wren_dpram_0 == 1'b1) && (rx_address_dpram == 6'd0))
        buffer_state[0] <= 1'b1;
    else
        buffer_state[0] <= buffer_state[0];
end
//B口缓存区状态
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        buffer_state[1] <= 1'b0;
    else if((c_state == B) && (ack == 1'b1))
        buffer_state[1] <= 1'b0;
    else if((c_state == A) && (ack == 1'b1) && (buffer_state[0] != 1'b1))
        buffer_state[1] <= 1'b0;
    else if((rx_wren_dpram_1 == 1'b1) && (rx_address_dpram == 6'd0))
        buffer_state[1] <= 1'b1;
    else
        buffer_state[1] <= buffer_state[1];
end
//一帧搬移完毕清空信号
// always @(posedge clk or negedge rst_n)
// begin
//     if(!rst_n)
//         slave_clr_a <= 1'b0;
//     else if((c_state == A) && (ack_pos == 1'b1))
//         slave_clr_a <= 1'b1;
//     else
//         slave_clr_a <= 1'b0;
// end

// always @(posedge clk or negedge rst_n)
// begin
//     if(!rst_n)
//         slave_clr_b <= 1'b0;
//     else if((c_state == B) && (ack_pos == 1'b1))
//         slave_clr_b <= 1'b1;
//     else
//         slave_clr_b <= 1'b0;
// end
//RAM读数据
assign qout = (c_state == B) ? dpram_B_q_b : dpram_A_q_b;
//rden MUX
assign rden_0 = (n_state == B) ? 1'b0 : rden;
assign rden_1 = (n_state == B) ? rden : 1'b0;
//数据帧有效 提前拉低 为了下一帧有效的判断 避免读空的情况
// assign frame_available = (buffer_state == 2'b00) ? 1'b0 : 1'b1;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        frame_available_0 <= 1'b0;
    end
    else if (rx_wren_dpram_0 && (rx_address_dpram == 6'd0)) begin
        frame_available_0 <= 1'b1;
    end
    else if ((rden_0 == 1'b1) && (address == 6'b0)) begin
        frame_available_0 <= 1'b0;
    end
    else begin
        frame_available_0 <= frame_available_0;
    end
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        frame_available_1 <= 1'b0;
    end
    else if (rx_wren_dpram_1 && (rx_address_dpram == 6'd0)) begin
        frame_available_1 <= 1'b1;
    end
    else if ((rden_1 == 1'b1) && (address == 6'b0)) begin
        frame_available_1 <= 1'b0;
    end
    else begin
        frame_available_1 <= frame_available_1;
    end
end
assign frame_available = frame_available_0 || frame_available_1;
// assign frame_available = frame_available_tmp & (~ack);
//缓存区可用
// always @(posedge clk or negedge rst_n) begin
//     if (!rst_n) begin
//         buffer_available <= 1'b0;
//     end
//     else if (buffer_state == 2'b11) begin
//         buffer_available <= 1'b0;
//     end
//     else begin
//         buffer_available <= 1'b1;
//     end
// end
//21.6.27改为组合逻辑
assign buffer_available = (buffer_state == 2'b11) ? 1'b0 : 1'b1;

//打一拍写通道内容
// always @(posedge clk or negedge rst_n) begin
//     if (!rst_n) begin
//         rx_wren_dpram_0_ff1 <= 1'b0;
//         rx_wren_dpram_1_ff1 <= 1'b0;
//         rx_address_dpram_ff1 <= 6'b0;
//         rx_data_dpram_0_ff1 <= 256'b0;
//         rx_data_dpram_1_ff1 <= 256'b0;
//     end
//     else begin
//         rx_wren_dpram_0_ff1 <= rx_wren_dpram_0;
//         rx_wren_dpram_1_ff1 <= rx_wren_dpram_1;
//         rx_address_dpram_ff1 <= rx_address_dpram;
//         rx_data_dpram_0_ff1 <= rx_data_dpram_0;
//         rx_data_dpram_1_ff1 <= rx_data_dpram_1;
//     end
// end

//例化的简单双口RAM
`ifdef ASIC
ram_2p_d64_w256_wrapper U_cross_ram_A_asic(
 //.clka(clk),
 .clk(clk),
 .ram_2p_cfg_register(ram_2p_cfg_register),
 .wren(rx_wren_dpram_0),
 .waddr(rx_address_dpram),
 .wdata(rx_data_dpram_0),
 //.clkb(clk),
 .rden(rden_0),
 .raddr(address),
 .rdata(dpram_A_q_b)   
);
ram_2p_d64_w256_wrapper U_cross_ram_B_asic(
 //.clka(clk),
 .clk(clk),
 .ram_2p_cfg_register(ram_2p_cfg_register),
 .wren(rx_wren_dpram_1),
 .waddr(rx_address_dpram),
 .wdata(rx_data_dpram_1),
 //.clkb(clk),
 .rden(rden_1),
 .raddr(address),
 .rdata(dpram_B_q_b)   
);
`else
cross_ram cross_ram_A (
  .clka(clk),    // input wire clka
  .wea(rx_wren_dpram_0),      // input wire [0 : 0] wea
  .addra(rx_address_dpram),  // input wire [5 : 0] addra
  .dina(rx_data_dpram_0),    // input wire [255 : 0] dina
  .clkb(clk),    // input wire clkb
  .addrb(address),  // input wire [5 : 0] addrb
  .doutb(dpram_A_q_b)  // output wire [255 : 0] doutb
);

cross_ram cross_ram_B (
  .clka(clk),    // input wire clka
  .wea(rx_wren_dpram_1),      // input wire [0 : 0] wea
  .addra(rx_address_dpram),  // input wire [5 : 0] addra
  .dina(rx_data_dpram_1),    // input wire [255 : 0] dina
  .clkb(clk),    // input wire clkb
  .addrb(address),  // input wire [5 : 0] addrb
  .doutb(dpram_B_q_b)  // output wire [255 : 0] doutb
);
`endif

endmodule
